﻿<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>ComValue - 语法 &amp; 使用 | AutoHotkey v2</title>
<meta name="description" content="The ComValue class wraps a value, SafeArray or COM object for use by the script or for passing to a COM method." />
<meta name="ahk:equiv-v1" content="lib/ComObjActive.htm" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="../static/theme.css" rel="stylesheet" type="text/css" />
<script src="../static/content.js" type="text/javascript"></script>
</head>
<body>

<h1>ComValue</h1>

<p>包装一个值, 安全数组或 COM 对象, 以供脚本使用或传递给 COM 方法.</p>
<pre class="Syntax">ComObj := <span class="func">ComValue</span>(VarType, Value <span class="optional">, Flags</span>)</pre>
<p><code>ComValue</code> 本身是一个派生自 <code>Any</code> 的<a href="Class.htm">类</a>, 但只用于创建或识别 COM 封装对象.</p>

<h2 id="Parameters">参数</h2>
<dl>

  <dt>VarType</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#numbers">整数</a></p>
    <p>表示值类型的整数. 类型列表见 <a href="ComObjType.htm#vt">ComObjType</a>.</p>
  </dd>

  <dt>Value</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#numbers">整数</a></p>
    <p>要包装的值.</p>
    <p>如果这是一个纯整数且 <em>VarType</em> 不是 VT_R4, VT_R8, VT_DATE 或 VT_CY, 则直接使用其值; 特别是 VT_BSTR, VT_DISPATCH 和 VT_UNKNOWN 可以用一个指针值来初始化.</p>
    <p>在任何其他情况下, 使用与普通 COM 方法调用相同的规则将值复制到临时 VARIANT 中. 如果源变量类型不等于 <em>VarType</em>, 则通过调用 <em>wFlags</em> 值为 0 的 <a href="https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-variantchangetype">VariantChangeType</a> 来尝试转换. 如果转换失败, 则抛出异常.</p>
  </dd>

  <dt>Flags</dt>
  <dd>
    <p>类型: <a href="../Concepts.htm#numbers">整数</a></p>
    <p>影响包装器对象行为的标志; 有关详情, 请参阅 <a href="ComObjFlags.htm">ComObjFlags</a>.</p>
  </dd>

</dl>

<h2 id="Returns">返回值</h2>
<p>类型: ComValue, ComValueRef, <a href="ComObjArray.htm">ComObjArray</a> 或 <a href="ComObject.htm">ComObject</a></p>
<p>返回包含<a href="ComObjType.htm#vt">变体类型</a>和值或指针的封装对象.</p>
<p>该对象有多个用途:</p>
<ol>
  <li>某些 COM 方法可能需要特定类型的值, 这些值在 AutoHotkey 中没有直接等效的值. 此函数允许在将值传递给 COM 方法时指定值的类型. 例如, <code>ComValue(0xB, -1)</code> 创建一个表示 COM 布尔值为 <em>true</em> 的对象.</li>
  <li>通过封装 COM 对象或 SafeArray, 脚本可以使用<a href="../Objects.htm#Usage_Objects">对象语法</a>更自然地与其交互. 但是, 大多数脚本不需要手动执行此操作, 因为封装对象是由 <a href="ComObject.htm">ComObject</a>, <a href="ComObjArray.htm">ComObjArray</a>, <a href="ComObjActive.htm">ComObjActive</a>, <a href="ComObjGet.htm">ComObjGet</a> 和任何返回对象的 COM 方法自动创建的.</li>
  <li>封装 COM 接口指针允许脚本利用自动引用计数. 接口指针可以在返回到脚本时立即包装(通常是从 <a href="ComCall.htm">ComCall</a> 或 <a href="DllCall.htm">DllCall</a> 返回), 避免在稍后的某个点需要显式<a href="ObjAddRef.htm">释放</a>它.</li>
</ol>

<h2 id="Ptr">Ptr</h2>
<p>如果一个封装对象的 <a href="ComObjType.htm#vt"><em>VarType</em></a> 是 VT_UNKNOWN(13) 或包含 VT_BYREF(0x4000) 或 VT_ARRAY(0x2000) 标志, 则 <code>Ptr</code> 属性可用于检索对象, 类型化变量或安全数组的地址. 这允许将 ComObject 本身传递给任何具有 <code>"Ptr"</code> 类型的 <a href="DllCall.htm">DllCall</a> 或 <a href="ComCall.htm">ComCall</a> 参数, 但也可以显式地使用. 例如, 在这些情况下, <code>ComObj.Ptr</code> 等同于 <code>ComObjValue(ComObj)</code>.</p>
<p>如果一个封装对象的 <a href="ComObjType.htm#vt"><em>VarType</em></a> 是 VT_UNKNOWN(13) 或 VT_DISPATCH(9) 并且封装指针为 null(0), 则可以使用 <code>Ptr</code> 属性来检索当前的 null 值, 或者为封装对象赋值一个指针. 一旦被赋值(如果非-null), 当封装对象被释放时, 指针将被自动释放. 这可以与类型为 <code>"Ptr*"</code> 或 <code>"PtrP"</code> 的 <a href="DllCall.htm">DllCall</a> 或 <a href="ComCall.htm">ComCall</a> 输出参数一起使用, 以确保指针会被自动释放, 例如当发生错误时. 有关示例, 请参阅 <a href="ComObjQuery.htm#ExIE">ComObjQuery</a>.</p>
<p>如果一个包装器对象的 <em>VarType</em> 是 VT_DISPATCH(9) 并且为 null(0) 指针值赋值为非-null 指针值, 其类型从 <code>ComValue</code> 变为 <code>ComObject</code>. 包装对象的属性和方法变得可用, 而 <code>Ptr</code> 属性变为不可用.</p>

<h2 id="ByRef">ByRef</h2>
<p>如果包装器对象的 <a href="ComObjType.htm#vt"><em>VarType</em></a> 包含 VT_BYREF(0x4000) 标志, 可以用空的方括号对 <code>[]</code> 来读写引用的值.</p>
<p>当创建一个引用, <em>Value</em> 须为变量的内存地址, 或足够存储指定类型的值的缓冲. 例如, 下面的代码可以用来创建一个 VBScript 函数可以写入的变量:</p>
<pre>vbuf := Buffer(24, 0)
vref := ComValue(0x400C, vbuf.ptr)  <em>; 0x400C 为 VT_BYREF 与 VT_VARIANT 组合而得.</em>

vref[] := "in value"
sc.Run("Example", vref)  <em>; sc  应像<a href="#ExByRef">下面例子</a>一样进行初始化.</em>
MsgBox vref[]</pre>
<p>请注意, 尽管在通过 <code>vref[]</code> 或 COM 方法分配新值时, 任何先前的值都会被释放, 但最终的值不会被自动释放. 释放该值需要知道它是哪种类型. 因为在这种情况下它是 VT_VARIANT, 它可以通过用 <a href="DllCall.htm">DllCall</a> 调用 <a href="https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-variantclear">VariantClear</a> 来释放, 或者使用更简单的方法: 分配一个整数, 如 <code>vref[] := 0</code>.</p>
<p>如果方法接受如上所示的 VT_BYREF 和 VT_VARIANT 的组合, 可以使用 <a href="../Concepts.htm#variable-references">VarRef</a> 代替. 例如:</p>
<pre>some_var := "in value"
sc.Run("Example", &amp;some_var)
MsgBox some_var</pre>
<p>然而, 有些方法需要更特定的变体类型, 如 <code>VT_BYREF | VT_I4</code>. 在这种情况下, 必须使用上面所示的第一种方法, 用适当的变体类型替换 0x400C.</p>

<h2 id="Remarks">一般说明</h2>
<p>当此函数用于包装 <a href="http://msdn.microsoft.com/en-us/library/dd318520.aspx">IDispatch</a> 或 IUnknown 接口指针(以整数形式传递) 时, 包装器对象负责在适当的时候自动释放指针. 因此, 如果脚本打算在调用这个函数之后使用指针, 它必须首先调用 <code><a href="ObjAddRef.htm">ObjAddRef</a>(DispPtr)</code>. 相比之下, 如果 <em>Value</em> 本身是 ComValue 或 ComObject, 则没有必要这样做.</p>
<p>从 VT_UNKNOWN 到 VT_DISPATCH 的转换会导致对 <a href="https://docs.microsoft.com/en-au/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)">IUnknown::QueryInterface</a> 的调用, 这可能会产生一个不同于原来的接口指针, 并且如果对象没有实现 IDispatch, 会抛出异常 . 相反, 如果 <em>Value</em> 是整数, and <em>VarType</em> is VT_DISPATCH, 则直接使用该值, 因此必须是一个 idispatch 兼容的接口指针.</p>
<p>可以使用 <a href="ComObjType.htm">ComObjType</a> 检索包装器对象的 <em>VarType</em>.</p>
<p>可以使用 <a href="ComObjValue.htm">ComObjValue</a> 检索包装器对象的 <em>Value</em>.</p>
<p><b>已知限制:</b> 每次包装 COM 对象时, 都会创建新的包装器对象. 如 <code>obj1 == obj2</code> 和 <code>array[obj1] := value</code> 这样的比较和赋值运算, 将这两个包装器对象视为唯一的, 即使它们包含相同的变体类型和值.</p>

<h2 id="Related">相关</h2>
<p><a href="ComObjFromPtr.htm">ComObjFromPtr</a>, <a href="ComObject.htm">ComObject</a>, <a href="ComObjGet.htm">ComObjGet</a>, <a href="ComObjConnect.htm">ComObjConnect</a>, <a href="ComObjFlags.htm">ComObjFlags</a>, <a href="ObjAddRef.htm">ObjAddRef/ObjRelease</a>, <a href="ComObjQuery.htm">ComObjQuery</a>, <a href="http://msdn.microsoft.com/en-us/library/ms221467.aspx">GetActiveObject (MSDN)</a></p>

<h2 id="Examples">示例</h2>
<div class="ex" id="ExByRef">
<p><a class="ex_number" href="#ExByRef"></a> 传递 VARIANT ByRef 给 COM 函数.</p>
<pre>
<em>; 条件 - ScriptControl 需 32 位版本的 AutoHotkey.</em>
code := "
(
Sub Example(Var)
    MsgBox Var
    Var = "out value!"
End Sub
)"
sc := ComObject("ScriptControl"), sc.Language := "VBScript", sc.AddCode(code)


<em>; 示例: 传递 VARIANT ByRef 至 COM 方法.</em>
var := ComVar()
var[] := "in value"
sc.Run("Example", var.ref)
MsgBox var[]

<em>; 同样的事情, 但更直接:</em>
variant_buf := Buffer(24, 0)  <em>; 使 VARIANT 的缓冲足够大.</em>
var := ComValue(0x400C, variant_buf.ptr)  <em>; 引用 VARIANT.</em>
var[] := "in value"
sc.Run("Example", var)  <em>; 传递 VT_BYREF ComValue 本身, 没有 [] 或 .ref.</em>
MsgBox var[]
<em>; 如果 VARIANT 包含一个字符串或对象, 必须显式地释放它
; 通过调用 VariantClear 或赋值一个纯数字值:</em>
var[] := 0

<em>; 当方法接受 VT_BYREF|VT_VARIANT 时的最简单方法:</em>
var := "in value"
sc.Run("Example", &amp;var)
MsgBox var


<em>; ComVar: 一个可以用来传递一个值 ByRef 的对象.
;   this[] 检索值.
;   this[] := Val 设置值.
;   this.ref 检索一个 ByRef 对象, 用于传递给一个 COM 方法.</em>
class ComVar {
    __new(vType := 0xC) {
        <em>; 为 VARIANT 分配内存, 以保存我们的值.
        ; 即使在 vType != VT_VARIANT 时也会使用 VARIANT, 这样 VariantClear 就可以被 __delete 使用.</em>
        this.var := Buffer(24, 0)
        <em>; 创建一个可以用来传递变量 ByRef 的对象.</em>
        this.ref := ComValue(0x4000|vType, this.var.ptr + (vType=0xC ? 0 : 8))
        <em>; 存储 VariantClear 的变体类型(如果不是 VT_VARIANT).</em>
        if Type != 0xC
            NumPut "ushort", vType, this.var
    }
    __item {
        get =&gt; this.ref[]
        set =&gt; this.ref[] := value
    }
    __delete() {
        DllCall("oleaut32\VariantClear", "ptr", this.var)
    }
}
</pre>
</div>

</body>
</html>